#include <Kernel/GlobalData.h>
#include <Memory/Paging.h>
#include <Utility/CLib.h>
#include <IntExcept/Error.h>

/*
@brief 为内核分配虚拟页面
@param `ncPages` 需要的虚拟页面数量
@param `dwAttributeBits` 虚拟页面属性位
@param `lpSave` 起始虚拟地址保存到此处
@return 状态码
@note PTE 第 9 位置为 1，P 位置 0
*/
LPVOID AllocatePageForKernel(int ncPages, DWORD dwAttrbuteBits)
{
    LPVOID lpRet = NULL; /* 分配的虚拟地址先置为 NULL */
    PageTableEntry* lpPageTableEntry = NULL; /* 页面 PTE 的地址 */
    /* 分页后，页表在逻辑上是连续的 */
    int nBegin = 256 + 1024 * 768; /* 从实模式 1 MiB 映射的虚拟地址空间之后开始查找 */
    int nEnd = nBegin;
    BOOL bSuccess = FALSE;
    /* 遍历页表，此过程中若页表缺失，则会触发 #PF，由中断处理程序补充该页表的定义 */
    while (nBegin < 1022 * 1024 && nEnd < 1022 * 1024) /* 内核页表一共 255 * 1024 个表项 */
    {
        
        if (nEnd - nBegin == ncPages)
        {
            bSuccess = TRUE;
            break; /* 找到了可连续分配的页面 */
        }
        else
        {
            if (0 == (g_MasterPageTable[nEnd / 1024][nEnd % 1024] & (PTE_PRESENT | PTE_ALLOCATED))) /* 页面可分配 */
            {
                nEnd++; /* 下次验证的页面 */
            }
            else /* 页面不可分配 */
            {
                nEnd++;
                nBegin = nEnd; /* 同时指向下个待检测位置 */
            }
        }
    }
    if (!bSuccess)
    {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return lpRet;
    }
    /* 找到连续的虚拟页面，预分配 */
    lpRet = (LPVOID)GENERATE_VIRTUAL_ADDRESS(nBegin / 1024, nBegin % 1024, 0); /* 由 PTE 序号得到 PTE 对应的虚拟地址 */
    while (nBegin < nEnd)
    {
        g_MasterPageTable[nBegin / 1024][nBegin % 1024] |= PTE_ALLOCATED | dwAttrbuteBits; /* 分配虚拟页面 */
        nBegin++;
    }
    SetLastError(ERROR_SUCCESS);
    return lpRet;
}